/*
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.beepmystuff;

import android.util.Log;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

/**
 * An implementation of {@link BmsHttpClient} as a thin wrapper around
 * {@link HttpClient}.
 */
public class DefaultBmsHttpClient implements BmsHttpClient {
  private static final String TAG = "BMS.BmsHttpClient";

  /** The default maximum allowed response size (10MB). */
  public static final long DEFAULT_MAX_RESPONSE_SIZE_BYTES = 10 * 1024 * 1024;

  private final HttpClient httpClient;

  /** The maximum allowed response size. */
  private long maxResponseSizeBytes = DEFAULT_MAX_RESPONSE_SIZE_BYTES;

  public DefaultBmsHttpClient(HttpClient httpClient) {
    this.httpClient = httpClient;
  }

  /**
   * Sets the maximum allowed response size, by default
   * {@link #DEFAULT_MAX_RESPONSE_SIZE_BYTES}.
   */
  public void setMaxResponseSize(long maxResponseSizeBytes) {
    this.maxResponseSizeBytes = maxResponseSizeBytes;
  }

  /**
   * Returns the maximum allowed response size, by default
   * {@link #DEFAULT_MAX_RESPONSE_SIZE_BYTES}.
   */
  public long getMaxResponseSize() {
    return maxResponseSizeBytes;
  }

  @Override
  public byte[] getResponseBody(HttpUriRequest request) throws BmsTransportException {
    Log.d(TAG, String.format("Req: %s %s", request.getMethod(), request.getURI()));

    HttpResponse response;
    try {
      response = httpClient.execute(request);
    } catch (ClientProtocolException e) {
      throw new BmsClientException(e.getMessage(), e);
    } catch (IOException e) {
      throw new BmsTransportException(e.getMessage(), e);
    }
    Log.d(TAG, String.format("Res: %s", response.getStatusLine()));

    if (response.getStatusLine().getStatusCode() != 200) {
      request.abort();  // free the connection.

      // Fatal if the server said it was our fault (4xx), a transport error if
      // it was the server's fault (5xx), and a client error otherwise.
      switch (response.getStatusLine().getStatusCode() / 100) {
        case 4:
          throw new BmsClientException("HTTP request returned " + response.getStatusLine());
        case 5:
          throw new BmsTransportException("HTTP request returned " + response.getStatusLine());
        default:
          throw new BmsClientException("Unexpected HTTP response " + response.getStatusLine());
      }
    }
    HttpEntity entity = response.getEntity();

    if (entity == null) {
      throw new BmsClientException("Received 200 OK with no content?!");
    }

    if (entity.getContentLength() > maxResponseSizeBytes) {
      request.abort();
      throw new BmsClientException(
          String.format("Response is %s bytes, larger than allowed (%s bytes)",
              entity.getContentLength(), maxResponseSizeBytes));
    }

    InputStream in = null;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    long total = 0;
    try {
      in = entity.getContent();
      byte[] buffer = new byte[4096];
      while (true) {
        int bytesRead = in.read(buffer);
        if (bytesRead == -1) {
          break;
        }

        total += bytesRead;
        if (total > maxResponseSizeBytes) {
          request.abort();
          throw new BmsClientException(
              String.format("Read %s response bytes, larger than allowed (%s bytes)",
                  total, maxResponseSizeBytes));
        }

        out.write(buffer, 0, bytesRead);
      }
    } catch (IOException e) {
      throw new BmsTransportException("Unable to read response from server", e);
    } finally {
      if (in != null) {
        try {
          // Triggers connection release for the HttpClient.
          in.close();
        } catch (IOException e) {
          // Really don't care.
        }
      }
    }

    return out.toByteArray();
  }

  @Override
  public String getResponseBodyAsString(HttpUriRequest request) throws BmsTransportException {
    byte[] response = getResponseBody(request);

    // TODO: work out how to get the correct charset from the response.
    try {
      return new String(response, "UTF-8");
    } catch (UnsupportedEncodingException impossible) {
      throw new AssertionError("UTF-8 must be supported. " + impossible.toString());
    }
  }
}
